import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import datetime as dt
import yfinance as yf
"ggplot")
plt.style.use(import statsmodels.api as sm
Capital Assets Pricing Model
Unsystematic risks can be diversified. Systematic risks can’t be diversified, \(\text{CAPM}\) measures this risk with \(\beta\).
\[ \begin{aligned} E(R_i)= & R_f+\beta_i[E(R_m)-R_f] \end{aligned} \]
\[ \begin{array} E\left(R_i\right) & =\text { capital asset expected return, e.g. a single stock or a portfolio } \\ R_f & =\text { risk-free return, e.g. 10y treasury bond} \\ \beta_i & =\text { sensitivity } \\ E\left(R_m\right) & =\text { expected return of the market, e.g. S&P500 } \end{array} \]
\(\beta_i\) can be calculated by OLS formula, i.e. \[ \beta= \frac{\text{Cov}(R_i, R_m)}{\text{Var}(R_m)} \] it tells how risky your portfolio is relative to the market, it only measures the systematic risk.
The portfolio \(\beta\) will be a weighted \(\beta\) \[ \beta = \sum_{i=1}^n w_i\beta_i \]
The model can be rewritten as \[\begin{aligned} E(R_i)-R_f= \alpha_i+\beta_i[E(R_m)-R_f] \end{aligned}\]to explicitly estimate \(\alpha\) as a constant. It characterizes the excess rate of return that
\[ \alpha_i= E(R_i)-\{R_f+\beta_i[E(R_m)-R_f]\} \] Usually the classic CAPM assumes \(\alpha=0\)
In short, \(\beta\) measures systematic risks, \(\alpha\) measure excess returns.
Implementation of CAPM
class CAPM:
def __init__(self, stocks, start_date, end_date):
self.stocks = stocks
self.start_date = start_date
self.end_date = end_date
def download_data(self):
= {}
stock_data for stock in self.stocks:
= yf.Ticker(stock)
ticker = ticker.history(
stock_data[stock] =self.start_date, end=self.end_date
start"Close"]
)[return pd.DataFrame(stock_data)
def resample_month(self):
# montly return gives a better normally distributed results
= (
stock_data self.download_data()
# without this, 'stock_data' will be called before assignment
) = stock_data.resample("M").last()
stock_data self.data = pd.DataFrame(
{"s_adjclose": stock_data[self.stocks[0]],
"m_adjclose": stock_data[self.stocks[1]],
}
)self.data[["s_logreturn", "m_logreturn"]] = np.log(self.data) - np.log(
self.data.shift()
)self.data = self.data.dropna()
return self.data
def calculate_beta(self):
= np.cov(self.data["s_logreturn"], self.data["m_logreturn"])
cov_matrix self.beta = cov_matrix[0, 1] / np.var(self.data["m_logreturn"])
print(self.beta)
return self.beta
def lin_reg(self):
= 0.05
R_F = 12
month_in_year
= self.data["m_logreturn"] - R_F
X = self.data["s_logreturn"] - R_F
Y = sm.add_constant(X) # adding a constant
X self.results = sm.OLS(Y, X).fit()
= self.results.params[0], self.results.params[1]
alpha, beta = R_F + beta * (
expected_return self.data["m_logreturn"].mean() * month_in_year - R_F
)self.plot_regression()
print("alpha: {}".format(alpha))
print("beta: {}".format(beta))
return alpha, beta, expected_return
def plot_regression(self):
= plt.subplots(figsize=(6, 6))
fig, ax
ax.scatter(self.data["m_logreturn"], self.data["s_logreturn"], c="CornflowerBlue"
)self.data["m_logreturn"], self.results.fittedvalues)
ax.plot("Capital Assets Pricing Model: {}".format(stocks[0]))
ax.set_title("Expected Stock Return")
ax.set_ylabel("Expected Market Return")
ax.set_xlabel(
plt.show()
if __name__ == "__main__":
= ["LULU", "^GSPC"]
stocks = CAPM(stocks, start_date="2010-01-01", end_date=dt.datetime.today())
capm = capm.download_data()
data = capm.resample_month()
data_month = capm.calculate_beta()
beta_calculated = capm.lin_reg() alpha, beta, exp_ret
1.2162665097364047
/tmp/ipykernel_3463/4094164405.py:21: FutureWarning:
'M' is deprecated and will be removed in a future version, please use 'ME' instead.
/tmp/ipykernel_3463/4094164405.py:48: FutureWarning:
Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
alpha: 0.014293598162767422
beta: 1.2096920961702622